﻿#pragma once

#include "string.h"
#include "checksum.h"
#include "mavlink_types.h"
#include "mavlink_conversions.h"
#include <stdio.h>

#ifndef MAVLINK_HELPER
#define MAVLINK_HELPER
#endif

#include "mavlink_sha256.h"

#ifdef MAVLINK_USE_CXX_NAMESPACE
namespace mavlink {
#endif

/*
 * Internal function to give access to the channel status for each channel
 */
#ifndef MAVLINK_GET_CHANNEL_STATUS
MAVLINK_HELPER mavlink_status_t* mavlink_get_channel_status(uint8_t chan)
{
#ifdef MAVLINK_EXTERNAL_RX_STATUS
    // No m_mavlink_status array defined in function,
    // has to be defined externally
#else
    static mavlink_status_t m_mavlink_status[MAVLINK_COMM_NUM_BUFFERS];
#endif
    return &m_mavlink_status[chan];
}
#endif

/*
 * Internal function to give access to the channel buffer for each channel
 */
#ifndef MAVLINK_GET_CHANNEL_BUFFER
MAVLINK_HELPER mavlink_message_t* mavlink_get_channel_buffer(uint8_t chan)
{
    
#ifdef MAVLINK_EXTERNAL_RX_BUFFER
    // No m_mavlink_buffer array defined in function,
    // has to be defined externally
#else
    static mavlink_message_t m_mavlink_buffer[MAVLINK_COMM_NUM_BUFFERS];
#endif
    return &m_mavlink_buffer[chan];
}
#endif // MAVLINK_GET_CHANNEL_BUFFER

/* Enable this option to check the length of each message.
    This allows invalid messages to be caught much sooner. Use if the transmission
    medium is prone to missing (or extra) characters (e.g. a radio that fades in
    and out). Only use if the channel will only contain messages types listed in
    the headers.
*/
//#define MAVLINK_CHECK_MESSAGE_LENGTH

/**
 * @brief Reset the status of a channel.
 */
MAVLINK_HELPER void mavlink_reset_channel_status(uint8_t chan)
{
    mavlink_status_t *status = mavlink_get_channel_status(chan);
    status->parse_state = MAVLINK_PARSE_STATE_IDLE;
}

#ifndef MAVLINK_NO_SIGN_PACKET
/**
 * @brief create a signature block for a packet
 */
MAVLINK_HELPER uint8_t mavlink_sign_packet(mavlink_signing_t *signing,
                       uint8_t signature[MAVLINK_SIGNATURE_BLOCK_LEN],
                       const uint8_t *header, uint8_t header_len,
                       const uint8_t *packet, uint8_t packet_len,
                       const uint8_t crc[2])
{
    mavlink_sha256_ctx ctx;
    union {
        uint64_t t64;
        uint8_t t8[8];
    } tstamp;
    if (signing == NULL || !(signing->flags & MAVLINK_SIGNING_FLAG_SIGN_OUTGOING)) {
        return 0;
    }
    signature[0] = signing->link_id;
    tstamp.t64 = signing->timestamp;
    memcpy(&signature[1], tstamp.t8, 6);
    signing->timestamp++;
    
    mavlink_sha256_init(&ctx);
    mavlink_sha256_update(&ctx, signing->secret_key, sizeof(signing->secret_key));
    mavlink_sha256_update(&ctx, header, header_len);
    mavlink_sha256_update(&ctx, packet, packet_len);
    mavlink_sha256_update(&ctx, crc, 2);
    mavlink_sha256_update(&ctx, signature, 7);
    mavlink_sha256_final_48(&ctx, &signature[7]);
    
    return MAVLINK_SIGNATURE_BLOCK_LEN;
}
#endif

/**
 * @brief Trim payload of any trailing zero-populated bytes (MAVLink 2 only).
 *
 * @param payload Serialised payload buffer.
 * @param length Length of full-width payload buffer.
 * @return Length of payload after zero-filled bytes are trimmed.
 */
MAVLINK_HELPER uint8_t _mav_trim_payload(const char *payload, uint8_t length)
{
    while (length > 1 && payload[length-1] == 0) {
        length--;
    }
    return length;
}

#ifndef MAVLINK_NO_SIGNATURE_CHECK
/**
 * @brief check a signature block for a packet
 */
MAVLINK_HELPER bool mavlink_signature_check(mavlink_signing_t *signing,
                        mavlink_signing_streams_t *signing_streams,
                        const mavlink_message_t *msg)
{
    if (signing == NULL) {
        return true;
    }
        const uint8_t *p = (const uint8_t *)&msg->magic;
    const uint8_t *psig = msg->signature;
        const uint8_t *incoming_signature = psig+7;
    mavlink_sha256_ctx ctx;
    uint8_t signature[6];
    uint16_t i;
        
    mavlink_sha256_init(&ctx);
    mavlink_sha256_update(&ctx, signing->secret_key, sizeof(signing->secret_key));
    mavlink_sha256_update(&ctx, p, MAVLINK_NUM_HEADER_BYTES);
    mavlink_sha256_update(&ctx, _MAV_PAYLOAD(msg), msg->len);
    mavlink_sha256_update(&ctx, msg->ck, 2);
    mavlink_sha256_update(&ctx, psig, 1+6);
    mavlink_sha256_final_48(&ctx, signature);
        if (memcmp(signature, incoming_signature, 6) != 0) {
                signing->last_status = MAVLINK_SIGNING_STATUS_BAD_SIGNATURE;
        return false;
    }

    // now check timestamp
    union tstamp {
        uint64_t t64;
        uint8_t t8[8];
    } tstamp;
    uint8_t link_id = psig[0];
    tstamp.t64 = 0;
    memcpy(tstamp.t8, psig+1, 6);

    if (signing_streams == NULL) {
                signing->last_status = MAVLINK_SIGNING_STATUS_NO_STREAMS;
                return false;
    }
    
    // find stream
    for (i=0; i<signing_streams->num_signing_streams; i++) {
        if (msg->sysid == signing_streams->stream[i].sysid &&
            msg->compid == signing_streams->stream[i].compid &&
            link_id == signing_streams->stream[i].link_id) {
            break;
        }
    }
    if (i == signing_streams->num_signing_streams) {
        if (signing_streams->num_signing_streams >= MAVLINK_MAX_SIGNING_STREAMS) {
            // over max number of streams
                        signing->last_status = MAVLINK_SIGNING_STATUS_TOO_MANY_STREAMS;
                        return false;
        }
        // new stream. Only accept if timestamp is not more than 1 minute old
        if (tstamp.t64 + 6000*1000UL < signing->timestamp) {
                        signing->last_status = MAVLINK_SIGNING_STATUS_OLD_TIMESTAMP;
                        return false;
        }
        // add new stream
        signing_streams->stream[i].sysid = msg->sysid;
        signing_streams->stream[i].compid = msg->compid;
        signing_streams->stream[i].link_id = link_id;
        signing_streams->num_signing_streams++;
    } else {
        union tstamp last_tstamp;
        last_tstamp.t64 = 0;
        memcpy(last_tstamp.t8, signing_streams->stream[i].timestamp_bytes, 6);
        if (tstamp.t64 <= last_tstamp.t64) {
            // repeating old timestamp
                        signing->last_status = MAVLINK_SIGNING_STATUS_REPLAY;
                        return false;
        }
    }

    // remember last timestamp
    memcpy(signing_streams->stream[i].timestamp_bytes, psig+1, 6);

    // our next timestamp must be at least this timestamp
    if (tstamp.t64 > signing->timestamp) {
        signing->timestamp = tstamp.t64;
    }
        signing->last_status = MAVLINK_SIGNING_STATUS_OK;
        return true;
}
#endif


/**
 * @brief Finalize a MAVLink message with channel assignment
 *
 * This function calculates the checksum and sets length and aircraft id correctly.
 * It assumes that the message id and the payload are already correctly set. This function
 * can also be used if the message header has already been written before (as in mavlink_msg_xxx_pack
 * instead of mavlink_msg_xxx_pack_headerless), it just introduces little extra overhead.
 *
 * @param msg Message to finalize
 * @param system_id Id of the sending (this) system, 1-127
 * @param length Message length
 */
MAVLINK_HELPER uint16_t mavlink_finalize_message_buffer(mavlink_message_t* msg, uint8_t system_id, uint8_t component_id,
                              mavlink_status_t* status, uint8_t min_length, uint8_t length, uint8_t crc_extra)
{
    bool mavlink1 = (status->flags & MAVLINK_STATUS_FLAG_OUT_MAVLINK1) != 0;
#ifndef MAVLINK_NO_SIGN_PACKET
    bool signing = 	(!mavlink1) && status->signing && (status->signing->flags & MAVLINK_SIGNING_FLAG_SIGN_OUTGOING);
#else
    bool signing = false;
#endif
    uint8_t signature_len = signing? MAVLINK_SIGNATURE_BLOCK_LEN : 0;
        uint8_t header_len = MAVLINK_CORE_HEADER_LEN+1;
    uint8_t buf[MAVLINK_CORE_HEADER_LEN+1];
    if (mavlink1) {
        msg->magic = MAVLINK_STX_MAVLINK1;
        header_len = MAVLINK_CORE_HEADER_MAVLINK1_LEN+1;
    } else {
        msg->magic = MAVLINK_STX;
    }
    msg->len = mavlink1?min_length:_mav_trim_payload(_MAV_PAYLOAD(msg), length);
    msg->sysid = system_id;
    msg->compid = component_id;
    msg->incompat_flags = 0;
    if (signing) {
        msg->incompat_flags |= MAVLINK_IFLAG_SIGNED;
    }
    msg->compat_flags = 0;
    msg->seq = status->current_tx_seq;
    status->current_tx_seq = status->current_tx_seq + 1;

    // form the header as a byte array for the crc
    buf[0] = msg->magic;
    buf[1] = msg->len;
    if (mavlink1) {
        buf[2] = msg->seq;
        buf[3] = msg->sysid;
        buf[4] = msg->compid;
        buf[5] = msg->msgid & 0xFF;
    } else {
        buf[2] = msg->incompat_flags;
        buf[3] = msg->compat_flags;
        buf[4] = msg->seq;
        buf[5] = msg->sysid;
        buf[6] = msg->compid;
        buf[7] = msg->msgid & 0xFF;
        buf[8] = (msg->msgid >> 8) & 0xFF;
        buf[9] = (msg->msgid >> 16) & 0xFF;
    }
    
    uint16_t checksum = crc_calculate(&buf[1], header_len-1);
    crc_accumulate_buffer(&checksum, _MAV_PAYLOAD(msg), msg->len);
    crc_accumulate(crc_extra, &checksum);
    mavlink_ck_a(msg) = (uint8_t)(checksum & 0xFF);
    mavlink_ck_b(msg) = (uint8_t)(checksum >> 8);

    msg->checksum = checksum;

#ifndef MAVLINK_NO_SIGN_PACKET
    if (signing) {
        mavlink_sign_packet(status->signing,
                    msg->signature,
                    (const uint8_t *)buf, header_len,
                    (const uint8_t *)_MAV_PAYLOAD(msg), msg->len,
                    (const uint8_t *)_MAV_PAYLOAD(msg)+(uint16_t)msg->len);
    }
#endif

    return msg->len + header_len + 2 + signature_len;
}

MAVLINK_HELPER uint16_t mavlink_finalize_message_chan(mavlink_message_t* msg, uint8_t system_id, uint8_t component_id,
                              uint8_t chan, uint8_t min_length, uint8_t length, uint8_t crc_extra)
{
    mavlink_status_t *status = mavlink_get_channel_status(chan);
    return mavlink_finalize_message_buffer(msg, system_id, component_id, status, min_length, length, crc_extra);
}

/**
 * @brief Finalize a MAVLink message with MAVLINK_COMM_0 as default channel
 */
MAVLINK_HELPER uint16_t mavlink_finalize_message(mavlink_message_t* msg, uint8_t system_id, uint8_t component_id, 
                         uint8_t min_length, uint8_t length, uint8_t crc_extra)
{
    return mavlink_finalize_message_chan(msg, system_id, component_id, MAVLINK_COMM_0, min_length, length, crc_extra);
}

static inline void _mav_parse_error(mavlink_status_t *status)
{
    status->parse_error++;
}

#ifdef MAVLINK_USE_CONVENIENCE_FUNCTIONS
MAVLINK_HELPER void _mavlink_send_uart(mavlink_channel_t chan, const char *buf, uint16_t len);

/**
 * @brief Finalize a MAVLink message with channel assignment and send
 */
MAVLINK_HELPER void _mav_finalize_message_chan_send(mavlink_channel_t chan, uint32_t msgid,
                                                    const char *packet, 
                            uint8_t min_length, uint8_t length, uint8_t crc_extra)
{
    uint16_t checksum;
    uint8_t buf[MAVLINK_NUM_HEADER_BYTES];
    uint8_t ck[2];
    mavlink_status_t *status = mavlink_get_channel_status(chan);
        uint8_t header_len = MAVLINK_CORE_HEADER_LEN;
    uint8_t signature_len = 0;
    uint8_t signature[MAVLINK_SIGNATURE_BLOCK_LEN];
    bool mavlink1 = (status->flags & MAVLINK_STATUS_FLAG_OUT_MAVLINK1) != 0;
    bool signing = 	(!mavlink1) && status->signing && (status->signing->flags & MAVLINK_SIGNING_FLAG_SIGN_OUTGOING);

        if (mavlink1) {
            length = min_length;
            if (msgid > 255) {
                // can't send 16 bit messages
                _mav_parse_error(status);
                return;
            }
            header_len = MAVLINK_CORE_HEADER_MAVLINK1_LEN;
            buf[0] = MAVLINK_STX_MAVLINK1;
            buf[1] = length;
            buf[2] = status->current_tx_seq;
            buf[3] = mavlink_system.sysid;
            buf[4] = mavlink_system.compid;
            buf[5] = msgid & 0xFF;
        } else {
        uint8_t incompat_flags = 0;
        if (signing) {
        incompat_flags |= MAVLINK_IFLAG_SIGNED;
        }
            length = _mav_trim_payload(packet, length);
            buf[0] = MAVLINK_STX;
            buf[1] = length;
            buf[2] = incompat_flags;
            buf[3] = 0; // compat_flags
            buf[4] = status->current_tx_seq;
            buf[5] = mavlink_system.sysid;
            buf[6] = mavlink_system.compid;
            buf[7] = msgid & 0xFF;
            buf[8] = (msgid >> 8) & 0xFF;
            buf[9] = (msgid >> 16) & 0xFF;
        }
    status->current_tx_seq++;
    checksum = crc_calculate((const uint8_t*)&buf[1], header_len);
    crc_accumulate_buffer(&checksum, packet, length);
    crc_accumulate(crc_extra, &checksum);
    ck[0] = (uint8_t)(checksum & 0xFF);
    ck[1] = (uint8_t)(checksum >> 8);

#ifndef MAVLINK_NO_SIGN_PACKET
    if (signing) {
        // possibly add a signature
        signature_len = mavlink_sign_packet(status->signing, signature, buf, header_len+1,
                            (const uint8_t *)packet, length, ck);
    }
#endif

    MAVLINK_START_UART_SEND(chan, header_len + 3 + (uint16_t)length + (uint16_t)signature_len);
    _mavlink_send_uart(chan, (const char *)buf, header_len+1);
    _mavlink_send_uart(chan, packet, length);
    _mavlink_send_uart(chan, (const char *)ck, 2);
    if (signature_len != 0) {
        _mavlink_send_uart(chan, (const char *)signature, signature_len);
    }
    MAVLINK_END_UART_SEND(chan, header_len + 3 + (uint16_t)length + (uint16_t)signature_len);
}

/**
 * @brief re-send a message over a uart channel
 * this is more stack efficient than re-marshalling the message
 * If the message is signed then the original signature is also sent
 */
MAVLINK_HELPER void _mavlink_resend_uart(mavlink_channel_t chan, const mavlink_message_t *msg)
{
    uint8_t ck[2];

    ck[0] = (uint8_t)(msg->checksum & 0xFF);
    ck[1] = (uint8_t)(msg->checksum >> 8);
    // XXX use the right sequence here

        uint8_t header_len;
        uint8_t signature_len;
        
        if (msg->magic == MAVLINK_STX_MAVLINK1) {
            header_len = MAVLINK_CORE_HEADER_MAVLINK1_LEN + 1;
            signature_len = 0;
            MAVLINK_START_UART_SEND(chan, header_len + msg->len + 2 + signature_len);
            // we can't send the structure directly as it has extra mavlink2 elements in it
            uint8_t buf[MAVLINK_CORE_HEADER_MAVLINK1_LEN + 1];
            buf[0] = msg->magic;
            buf[1] = msg->len;
            buf[2] = msg->seq;
            buf[3] = msg->sysid;
            buf[4] = msg->compid;
            buf[5] = msg->msgid & 0xFF;
            _mavlink_send_uart(chan, (const char*)buf, header_len);
        } else {
            header_len = MAVLINK_CORE_HEADER_LEN + 1;
            signature_len = (msg->incompat_flags & MAVLINK_IFLAG_SIGNED)?MAVLINK_SIGNATURE_BLOCK_LEN:0;
            MAVLINK_START_UART_SEND(chan, header_len + msg->len + 2 + signature_len);
            uint8_t buf[MAVLINK_CORE_HEADER_LEN + 1];
            buf[0] = msg->magic;
            buf[1] = msg->len;
            buf[2] = msg->incompat_flags;
            buf[3] = msg->compat_flags;
            buf[4] = msg->seq;
            buf[5] = msg->sysid;
            buf[6] = msg->compid;
            buf[7] = msg->msgid & 0xFF;
            buf[8] = (msg->msgid >> 8) & 0xFF;
            buf[9] = (msg->msgid >> 16) & 0xFF;
            _mavlink_send_uart(chan, (const char *)buf, header_len);
        }
    _mavlink_send_uart(chan, _MAV_PAYLOAD(msg), msg->len);
    _mavlink_send_uart(chan, (const char *)ck, 2);
        if (signature_len != 0) {
        _mavlink_send_uart(chan, (const char *)msg->signature, MAVLINK_SIGNATURE_BLOCK_LEN);
        }
        MAVLINK_END_UART_SEND(chan, header_len + msg->len + 2 + signature_len);
}
#endif // MAVLINK_USE_CONVENIENCE_FUNCTIONS

/**
 * @brief Pack a message to send it over a serial byte stream
 */
MAVLINK_HELPER uint16_t mavlink_msg_to_send_buffer(uint8_t *buf, const mavlink_message_t *msg)
{
    uint8_t signature_len, header_len;
    uint8_t *ck;
        uint8_t length = msg->len;
        
    if (msg->magic == MAVLINK_STX_MAVLINK1) {
        signature_len = 0;
        header_len = MAVLINK_CORE_HEADER_MAVLINK1_LEN;
        buf[0] = msg->magic;
        buf[1] = length;
        buf[2] = msg->seq;
        buf[3] = msg->sysid;
        buf[4] = msg->compid;
        buf[5] = msg->msgid & 0xFF;
        memcpy(&buf[6], _MAV_PAYLOAD(msg), msg->len);
        ck = buf + header_len + 1 + (uint16_t)msg->len;
    } else {
        length = _mav_trim_payload(_MAV_PAYLOAD(msg), length);
        header_len = MAVLINK_CORE_HEADER_LEN;
        buf[0] = msg->magic;
        buf[1] = length;
        buf[2] = msg->incompat_flags;
        buf[3] = msg->compat_flags;
        buf[4] = msg->seq;
        buf[5] = msg->sysid;
        buf[6] = msg->compid;
        buf[7] = msg->msgid & 0xFF;
        buf[8] = (msg->msgid >> 8) & 0xFF;
        buf[9] = (msg->msgid >> 16) & 0xFF;
        memcpy(&buf[10], _MAV_PAYLOAD(msg), length);
        ck = buf + header_len + 1 + (uint16_t)length;
        signature_len = (msg->incompat_flags & MAVLINK_IFLAG_SIGNED)?MAVLINK_SIGNATURE_BLOCK_LEN:0;
    }
    ck[0] = (uint8_t)(msg->checksum & 0xFF);
    ck[1] = (uint8_t)(msg->checksum >> 8);
    if (signature_len > 0) {
        memcpy(&ck[2], msg->signature, signature_len);
    }

    return header_len + 1 + 2 + (uint16_t)length + (uint16_t)signature_len;
}

union __mavlink_bitfield {
    uint8_t uint8;
    int8_t int8;
    uint16_t uint16;
    int16_t int16;
    uint32_t uint32;
    int32_t int32;
};


MAVLINK_HELPER void mavlink_start_checksum(mavlink_message_t* msg)
{
    uint16_t crcTmp = 0;
    crc_init(&crcTmp);
    msg->checksum = crcTmp;
}

// 更新消息的 CRC 校验和，每收到一个字节，就要更新一次；
MAVLINK_HELPER void mavlink_update_checksum(mavlink_message_t* msg, uint8_t c)
{
    // 取出当前消息 msg 里的已有的 CRC 校验值，保存到一个临时变量 checksum 里；
    uint16_t checksum = msg->checksum;
    // 调用 crc_accumulate 函数，把当前字节 c 加到 CRC 里面去；
    crc_accumulate(c, &checksum);
    // 把新的 CRC 结果，再存回到消息 msg->checksum 字段里；
    msg->checksum = checksum;
}

#ifndef MAVLINK_GET_MSG_ENTRY
// 用来根据消息 ID (msgid) 找到对应的消息定义 mavlink_msg_entry_t（比如最大长度、CRC 附加码等）；
// static：
//   - 只在当前 .c 文件里可见，别人其他 .c 文件引用不到这个函数；
//   - 防止链接时重名冲突；
// inline：
//   - 提示编译器把函数代码直接插到调用处，避免函数调用的开销（少一次跳转开销，加快执行速度）；
//   - 适合写小函数，比如：加减乘除，取数组元素，更新校验和这种小操作；
//   - 希望执行特别快（因为消息解析是很高频的操作）；
// const：
//   - 常量，不可修改；
MAVLINK_HELPER const mavlink_msg_entry_t *mavlink_get_msg_entry(uint32_t msgid)
{
    // 定义了一个静态常量数组 mavlink_message_crcs；
    // 这个数组保存了所有支持的 MAVLink 消息的基本信息，比如每条消息的 ID、最大长度、CRC extra；
    // 具体内容是由宏 MAVLINK_MESSAGE_CRCS 生成的，里面是所有 MAVLink 消息的列表；
    static const mavlink_msg_entry_t mavlink_message_crcs[] = MAVLINK_MESSAGE_CRCS;
    // 使用二分查找（bisection search）来找对应的 msgid；
    // 要求数组是按照 msgid 排好序的，不然二分查找就错了；
    //   - low 是下边界索引（初始是 0）；
    //   - high 是上边界索引（初始是数组最后一个元素的索引）；
    //   - sizeof(mavlink_message_crcs)/sizeof(mavlink_message_crcs[0]) 是数组元素个数；
    uint32_t low=0, high=sizeof(mavlink_message_crcs)/sizeof(mavlink_message_crcs[0]) - 1;
    // 当 low 小于 high 时循环，标准的二分查找；
    while (low < high) {
        // mid 是中间位置（注意这里是 (low+1+high)/2，不是普通的 (low+high)/2，稍微偏向右边一点点，避免死循环）；
        uint32_t mid = (low+1+high)/2;
        // 如果要找的 msgid 比中间的小；
        if (msgid < mavlink_message_crcs[mid].msgid) {
            // 缩小搜索范围到前半段：high = mid-1；
            high = mid-1;
            continue;
        }
        // 如果要找的 msgid 比中间的大；
        if (msgid > mavlink_message_crcs[mid].msgid) {
            // 缩小搜索范围到后半段：low = mid；
            low = mid;
            continue;
        }
        low = mid;
        break;
    }
    // 如果 low 位置的 msgid 不等于要找的 msgid，说明根本没找到；
    if (mavlink_message_crcs[low].msgid != msgid) {
        // msgid is not in the table
        return NULL;
    }
    // 返回指向这个消息定义 (mavlink_msg_entry_t) 的指针；
    return &mavlink_message_crcs[low];
}
#endif

/*
  return the crc_extra value for a message
*/
MAVLINK_HELPER uint8_t mavlink_get_crc_extra(const mavlink_message_t *msg)
{
    const mavlink_msg_entry_t *e = mavlink_get_msg_entry(msg->msgid);
    return e?e->crc_extra:0;
}

/*
  return the min message length
*/
#define MAVLINK_HAVE_MIN_MESSAGE_LENGTH
MAVLINK_HELPER uint8_t mavlink_min_message_length(const mavlink_message_t *msg)
{
    const mavlink_msg_entry_t *e = mavlink_get_msg_entry(msg->msgid);
        return e?e->min_msg_len:0;
}

/*
  return the max message length (including extensions)
*/
#define MAVLINK_HAVE_MAX_MESSAGE_LENGTH
MAVLINK_HELPER uint8_t mavlink_max_message_length(const mavlink_message_t *msg)
{
    const mavlink_msg_entry_t *e = mavlink_get_msg_entry(msg->msgid);
        return e?e->max_msg_len:0;
}

/**
 * This is a variant of mavlink_frame_char() but with caller supplied
 * parsing buffers. It is useful when you want to create a MAVLink
 * parser in a library that doesn't use any global variables
 *
 * @param rxmsg    parsing message buffer
 * @param status   parsing status buffer
 * @param c        The char to parse
 *
 * @param r_message NULL if no message could be decoded, otherwise the message data
 * @param r_mavlink_status if a message was decoded, this is filled with the channel's stats
 * @return 0 if no message could be decoded, 1 on good message and CRC, 2 on bad CRC
 *
 */
// 这是一版 mavlink_frame_char 的变体，由调用者自己传入 buffer（而不是自己去根据通道号找全局变量）；
// 适合你在写库的时候用，因为不依赖全局变量，可以更灵活地在多个地方同时解析 MAVLink 消息，互不干扰；
// 
// rxmsg：传进来的消息缓冲区，用来积攒字节，准备组装成一条完整 MAVLink 消息；
// status：传进来的解析状态机状态，用来记录目前解析到哪里了，是头、是数据、是校验等等；
// c：要处理的当前单个字节；
// r_message：输出，如果成功解出一条完整的 MAVLink 消息，放到这里，如果解析失败，就会是 NULL（或者没更新）；
// r_mavlink_status：输出，如果解析成功，会把这条消息相关的状态统计信息写到这里（比如成功消息数、丢包数等等）；
// 
// 返回值：
//   - 0：没解析出来消息（继续收）；
//   - 1：解析成功，校验通过；
//   - 2：解析成功，但是 CRC 校验错了（说明数据传输过程中出错了）；
MAVLINK_HELPER uint8_t mavlink_frame_char_buffer(mavlink_message_t* rxmsg, 
                                                 mavlink_status_t* status,
                                                 uint8_t c, 
                                                 mavlink_message_t* r_message, 
                                                 mavlink_status_t* r_mavlink_status)
{
    // 每次进来一个字节时，先默认设成消息还没完成；
    // 直到整个一包数据收完整，才会把它变成 MAVLINK_FRAMING_OK；
    status->msg_received = MAVLINK_FRAMING_INCOMPLETE;

    // parse_state 表示当前解析到哪一步；
    // 每收到一个字节，就根据当前状态+收到的字节，跳转到下一个状态；
    switch (status->parse_state)
    {
    // 空闲状态，还什么都没开始解析；
    case MAVLINK_PARSE_STATE_UNINIT:
    case MAVLINK_PARSE_STATE_IDLE:
        // 当前字节是 MAVLink v2 的帧头，就认为是一包新消息开始了；
        if (c == MAVLINK_STX)
        {
            // 解析状态机切换到 “已经收到 STX（帧头）；
            status->parse_state = MAVLINK_PARSE_STATE_GOT_STX;
            // 清空消息长度；
            rxmsg->len = 0;
            // 记录魔数（魔术字节，告诉别人你是 MAVLink 消息）；
            rxmsg->magic = c;
            // 把 flags里 MAVLINK1 标志清掉（默认是 MAVLink2）；
            status->flags &= ~MAVLINK_STATUS_FLAG_IN_MAVLINK1;
            // 计算 CRC；
            mavlink_start_checksum(rxmsg);
        // 如果是 MAVLink v1 的帧头（不同的 STX）；
        } else if (c == MAVLINK_STX_MAVLINK1)
        {
            status->parse_state = MAVLINK_PARSE_STATE_GOT_STX;
            rxmsg->len = 0;
            rxmsg->magic = c;
            status->flags |= MAVLINK_STATUS_FLAG_IN_MAVLINK1;
            mavlink_start_checksum(rxmsg);
        }
        break;

    // 已经收到 STX 头，下一个字节就是 Payload 长度（LEN 字段）；
    case MAVLINK_PARSE_STATE_GOT_STX:
        if (status->msg_received 
/* Support shorter buffers than the
   default maximum packet size */
#if (MAVLINK_MAX_PAYLOAD_LEN < 255)
            || c > MAVLINK_MAX_PAYLOAD_LEN
#endif
        )
        {
            // 出错了，buffer 溢出次数 +1；
            status->buffer_overrun++;
            // 调用 _mav_parse_error 更新解析错误计数；
            _mav_parse_error(status);
            // MAVLINK_FRAMING_INCOMPLETE 0
            status->msg_received = 0;
            // 把状态机回到空闲，准备下一包；
            status->parse_state = MAVLINK_PARSE_STATE_IDLE;
        }
        else
        {
            // NOT counting STX, LENGTH, SEQ, SYSID, COMPID, MSGID, CRC1 and CRC2
            // 记下 Payload 长度；
            rxmsg->len = c;
            // 重置数据位置 index 为 0；
            status->packet_idx = 0;
            // 把长度字段也加入 checksum 计算；
            mavlink_update_checksum(rxmsg, c);
            // 如果是 MAVLink1，它没有 incompat_flags/compat_flags 字段，所以直接跳到后续处理；
            if (status->flags & MAVLINK_STATUS_FLAG_IN_MAVLINK1) {
                rxmsg->incompat_flags = 0;
                rxmsg->compat_flags = 0;
                status->parse_state = MAVLINK_PARSE_STATE_GOT_COMPAT_FLAGS;
            // 如果是MAVLink2，后面要解析compat_flags字段，所以切到 GOT_LENGTH 或 GOT_COMPAT_FLAGS 状态；
            } else {
                status->parse_state = MAVLINK_PARSE_STATE_GOT_LENGTH;
            }
        }
        break;

    // 当状态机已经到了 MAVLINK_PARSE_STATE_GOT_LENGTH 后，收到下一个字节，继续解析；
    case MAVLINK_PARSE_STATE_GOT_LENGTH:
        // 对于 MAVLink 2，就是 incompat_flags 字段；
        // incompat_flags 是 MAVLink2 新增的字段，告诉你这个消息有没有什么不兼容的新特性；
        rxmsg->incompat_flags = c;
        // 检查一下：incompat_flags 有没有超出规范；
        // MAVLINK_IFLAG_MASK 是一个掩码，只允许支持的位被置位；
        // 如果出现了未知的 /incompatible 的 flag，说明这条消息本机无法正确处理；
        // & ~：清除（屏蔽）某些位；
        // 如果有不认识的 incompat_flags；
        if ((rxmsg->incompat_flags & ~MAVLINK_IFLAG_MASK) != 0) {
            // 记录一次解析错误；
            _mav_parse_error(status);
            // 认为这一包消息是坏的；
            status->msg_received = 0;
            // 丢弃它（回到 IDLE 状态，等下一包）；
            status->parse_state = MAVLINK_PARSE_STATE_IDLE;
            break;
        }
        // CRC 校验计算，MAVLink 是边接收边算 CRC 的；
        mavlink_update_checksum(rxmsg, c);
        // 状态机继续跳到下一个阶段：已经拿到 incomapt_flags，下一步准备收 compat_flags 字段了；
        status->parse_state = MAVLINK_PARSE_STATE_GOT_INCOMPAT_FLAGS;
        break;

    // 当前解析状态是已经收到了 incompat_flags 字节（即 MAVLink 协议扩展中的不兼容功能标志字段）；
    case MAVLINK_PARSE_STATE_GOT_INCOMPAT_FLAGS:
        // 把当前接收到的字节 c 存到消息 rxmsg->compat_flags 里；
        rxmsg->compat_flags = c;
        // 调用 mavlink_update_checksum，把这个字节也累加到校验和中（计算 CRC 用的）；
        mavlink_update_checksum(rxmsg, c);
        // 把解析状态更新为 MAVLINK_PARSE_STATE_GOT_COMPAT_FLAGS，表示下一步要解析 compat_flags（兼容标志）；
        status->parse_state = MAVLINK_PARSE_STATE_GOT_COMPAT_FLAGS;
        break;

    // 当前状态是已经收到了 compat_flags；
    case MAVLINK_PARSE_STATE_GOT_COMPAT_FLAGS:
        // 把当前字节 c 保存到消息的 seq 字段中，seq 是包的序列号，用来检测丢包；
        rxmsg->seq = c;
        // 更新 CRC 校验；
        mavlink_update_checksum(rxmsg, c);
        // 解析状态更新为 MAVLINK_PARSE_STATE_GOT_SEQ，准备接收 sysid（系统 ID）；
        status->parse_state = MAVLINK_PARSE_STATE_GOT_SEQ;
        break;
                
    // 当前状态是已经收到了序列号；
    case MAVLINK_PARSE_STATE_GOT_SEQ:
        // 把当前字节 c 保存到 sysid，也就是发送这条消息的系统 ID；
        rxmsg->sysid = c;
        mavlink_update_checksum(rxmsg, c);
        // 状态转到 MAVLINK_PARSE_STATE_GOT_SYSID，准备收 component ID（组件 ID）；
        status->parse_state = MAVLINK_PARSE_STATE_GOT_SYSID;
        break;

    // 当前状态是已经收到了 sysid；
    case MAVLINK_PARSE_STATE_GOT_SYSID:
        // 把当前字节 c 保存到 compid，表示发送消息的具体模块或组件的 ID，比如飞控系统内的飞控子模块、相机模块等；
        rxmsg->compid = c;
        mavlink_update_checksum(rxmsg, c);
        // 解析状态更新为 MAVLINK_PARSE_STATE_GOT_COMPID，接下来就要处理消息 ID 了；
        status->parse_state = MAVLINK_PARSE_STATE_GOT_COMPID;
        break;

    // 现在的状态是刚刚收到了 compid；
    case MAVLINK_PARSE_STATE_GOT_COMPID:
        // 当前字节 c 存到 rxmsg->msgid，也就是消息的编号（比如心跳包是 0，GPS 信息是 24 等等）；
        rxmsg->msgid = c;
        mavlink_update_checksum(rxmsg, c);
        // 检查当前协议是不是 MAVLink1 （早期的 MAVLink 版本）；
        // 因为 MAVLink1 的消息 ID 只有 1 个字节，MAVLink2 的消息 ID 是 3 个字节；
        if (status->flags & MAVLINK_STATUS_FLAG_IN_MAVLINK1) {
            // 消息长度 rxmsg->len > 0，说明后面有有效载荷（payload）；
            if(rxmsg->len > 0) {
                // 状态切换到 MAVLINK_PARSE_STATE_GOT_MSGID3，继续接收；
                status->parse_state = MAVLINK_PARSE_STATE_GOT_MSGID3;
            } else {
                // 长度为 0，直接跳到 MAVLINK_PARSE_STATE_GOT_PAYLOAD（没有 payload 的话，直接进入 payload 处理阶段）；
                status->parse_state = MAVLINK_PARSE_STATE_GOT_PAYLOAD;
            }
// 检查这个消息的长度是不是合理；
#ifdef MAVLINK_CHECK_MESSAGE_LENGTH
            // 如果比最小消息长度还小，或者比最大允许长度还大，就认为出错；
            if (rxmsg->len < mavlink_min_message_length(rxmsg) ||
                rxmsg->len > mavlink_max_message_length(rxmsg)) {
                // 增加错误计数器；
                _mav_parse_error(status);
                // 丢弃这条消息，回到挂起状态，重新开始接收；
                status->parse_state = MAVLINK_PARSE_STATE_IDLE;
                break;
            }
#endif
        // 不是 MAVLink1（那就是 MAVLink2）；
        } else {
            // 继续收消息 ID 的第 2 个字节（因为 MAVLink2 的消息 ID 是 3 字节的，需要多收 2 次）；
            // 状态切换到 MAVLINK_PARSE_STATE_GOT_MSGID1；
            status->parse_state = MAVLINK_PARSE_STATE_GOT_MSGID1;
        }
        break;

    // 当前状态是已经收到了 msgid 第 1 个字节；
    case MAVLINK_PARSE_STATE_GOT_MSGID1:
        // 再接收一个字节 c，这个字节是消息 ID 的第 2 个字节，需要左移 8 位（因为高位在后）；
        // 把它合并进 rxmsg->msgid（用 |= c<<8）；
        rxmsg->msgid |= c<<8;
        mavlink_update_checksum(rxmsg, c);
        // 状态切换到 MAVLINK_PARSE_STATE_GOT_MSGID2，准备继续收 msgid 第 3 个字节；
        status->parse_state = MAVLINK_PARSE_STATE_GOT_MSGID2;
        break;

    // 当前状态是：已经收到 msgid 的第 1 个字节和第 2 个字节了；
    case MAVLINK_PARSE_STATE_GOT_MSGID2:
        // 现在收到第3个字节 c，这是最高位，所以左移 16 位；
        rxmsg->msgid |= ((uint32_t)c)<<16;
        mavlink_update_checksum(rxmsg, c);
        if(rxmsg->len > 0){
            // 如果 rxmsg->len > 0，说明后面要接收 payload（数据），于是状态切到 GOT_MSGID3；
            status->parse_state = MAVLINK_PARSE_STATE_GOT_MSGID3;
        } else {
            // 如果长度为 0，说明这个消息没有 payload，直接进入 payload 完成状态 GOT_PAYLOAD；
            status->parse_state = MAVLINK_PARSE_STATE_GOT_PAYLOAD;
        }
// 检查这个消息的长度是否合法（在最小/最大允许范围内）；
#ifdef MAVLINK_CHECK_MESSAGE_LENGTH
        if (rxmsg->len < mavlink_min_message_length(rxmsg) ||
            rxmsg->len > mavlink_max_message_length(rxmsg))
        {
            _mav_parse_error(status);
            status->parse_state = MAVLINK_PARSE_STATE_IDLE;
            break;
        }
#endif
        break;
                
    // 当前状态是：完成接收 msgid 第 3 个字节，准备接收 payload 的字节；
    case MAVLINK_PARSE_STATE_GOT_MSGID3:
        // status->packet_idx 是当前收到了第几个 payload 字节，存完以后 ++，指向下一个；
        _MAV_PAYLOAD_NON_CONST(rxmsg)[status->packet_idx++] = (char)c;
        mavlink_update_checksum(rxmsg, c);
        // 判断是否已经收满了 payload；
        if (status->packet_idx == rxmsg->len)
        {
            // 状态切换到 GOT_PAYLOAD，准备下一步（收 CRC 校验码）；
            status->parse_state = MAVLINK_PARSE_STATE_GOT_PAYLOAD;
        }
        break;

    // 当前状态是：payload 收完了，现在准备处理 CRC 校验；
    case MAVLINK_PARSE_STATE_GOT_PAYLOAD: {
        // 通过 rxmsg->msgid 去找这条消息的定义 (mavlink_msg_entry_t)，比如最大长度、CRC 额外码等信息；
        // typedef struct __mavlink_msg_entry {
        //     uint32_t msgid;
        //     uint8_t crc_extra;
        //     uint8_t min_msg_len;            // minimum message length
        //     uint8_t max_msg_len;            // maximum message length (e.g. including mavlink2 extensions)
        //     uint8_t flags;                  // MAV_MSG_ENTRY_FLAG_*
        //     uint8_t target_system_ofs;      // payload offset to target_system, or 0
        //     uint8_t target_component_ofs;   // payload offset to target_component, or 0
        // } mavlink_msg_entry_t;
        const mavlink_msg_entry_t *e = mavlink_get_msg_entry(rxmsg->msgid);
        // 如果找到了定义 (e != NULL)，就拿出它的 crc_extra（一个 8 位整数，每种消息类型都特有的 CRC 校验附加码）；
        // 找不到定义的话，默认 crc_extra = 0；
        uint8_t crc_extra = e?e->crc_extra:0;
        // 把 crc_extra 也加进来一起算 CRC 校验和（MAVLink 的校验要求：消息内容 + crc_extra 一起算）；
        mavlink_update_checksum(rxmsg, crc_extra);
        // 检查 CRC 是否正确：
        //   - c 是收到的第一个 CRC 字节（低 8 位）；
        //   - rxmsg->checksum & 0xFF 是自己算出来的校验低 8 位；
        // 如果不一致，跳到 GOT_BAD_CRC1（校验错误）；
        if (c != (rxmsg->checksum & 0xFF)) {
            status->parse_state = MAVLINK_PARSE_STATE_GOT_BAD_CRC1;
        // 如果一致，跳到 GOT_CRC1（校验正确）；
        } else {
            status->parse_state = MAVLINK_PARSE_STATE_GOT_CRC1;
        }
        // 把收到的 CRC 第一个字节（低 8 位）存到 rxmsg->ck[0]；
        rxmsg->ck[0] = c;

        // 有些消息定义最大长度很长，但实际收到的 payload 比 max_msg_len 要短；
        // 从 payload[packet_idx] 开始，把后面没收到的部分全部用 0 填满，防止脏数据；
        if (e && status->packet_idx < e->max_msg_len) {
                memset(&_MAV_PAYLOAD_NON_CONST(rxmsg)[status->packet_idx], 0, e->max_msg_len - status->packet_idx);
        }
        break;
        }

    // 当前状态是刚收完 CRC 校验码的第 1 字节，无论 CRC 正确还是错误，都走这里；
    case MAVLINK_PARSE_STATE_GOT_CRC1:
    case MAVLINK_PARSE_STATE_GOT_BAD_CRC1:
        // 如果之前 CRC 第 1 个字节就错了；
        // 现在收到的 CRC 第 2 字节 c 和计算出来的不一样， 就说明整个消息 CRC 校验失败；
        if (status->parse_state == MAVLINK_PARSE_STATE_GOT_BAD_CRC1 || c != (rxmsg->checksum >> 8)) {
            // 收到的数据 CRC 错了；
            status->msg_received = MAVLINK_FRAMING_BAD_CRC;
        } else {
            // 收到的是完整正确的一帧数据；
            status->msg_received = MAVLINK_FRAMING_OK;
        }
        // 把收到的第 2 字节 CRC 存到 rxmsg 里；
        rxmsg->ck[1] = c;

        // 这条消息是不是带签名的消息（比如 MAVLink 2.0 的安全机制）；
        if (rxmsg->incompat_flags & MAVLINK_IFLAG_SIGNED) {
            // #define MAVLINK_SIGNATURE_BLOCK_LEN 13
            // 进入等签名数据的状态，一般需要再收 13 个字节的签名；
            status->parse_state = MAVLINK_PARSE_STATE_SIGNATURE_WAIT;
            // 如果 CRC 没坏，状态先标成未完成（等签名块收完再说）；
            status->signature_wait = MAVLINK_SIGNATURE_BLOCK_LEN;

            if (status->msg_received != MAVLINK_FRAMING_BAD_CRC) {
                // 如果 CRC 没错，则把当前消息标记为“未完成”（INCOMPLETE），表示还有签名没收完；
                status->msg_received = MAVLINK_FRAMING_INCOMPLETE;
            }
            // 如果 CRC 已错，就别改 msg_received 状态了，避免把“坏数据”误标成合法；
        // 这一分支处理非签名消息的情况（即不带签名的消息）；
        } else {
            // 如果系统启用了签名校验机制（status->signing 非空）；
            if ((status->signing) &&
                // 没有设置“允许接受非签名消息”的回调；
                (status->signing->accept_unsigned_callback == NULL ||
                // 表示不接受未签名的该消息类型；
                !status->signing->accept_unsigned_callback(status, rxmsg->msgid))) {
                // 这条未签名的消息就不被接受；
                if (status->msg_received != MAVLINK_FRAMING_BAD_CRC) {
                    // 如果 CRC 没错，就把当前消息标记为 BAD_SIGNATURE（签名错误）；
                    status->msg_received = MAVLINK_FRAMING_BAD_SIGNATURE;
                }
            }
            // 无论如何都回到初始状态，准备解析下一条消息；
            status->parse_state = MAVLINK_PARSE_STATE_IDLE;
            if (r_message != NULL) {
                //  如果 r_message 非空（即调用者传了指针），则将已解析的 rxmsg 内容拷贝出去，供外部使用；
                memcpy(r_message, rxmsg, sizeof(mavlink_message_t));
            }
        }
        break;

    // 签名等待状态；
    // 收集签名的 13 个字节，然后验证签名是否有效，决定是否接受这条消息；
    case MAVLINK_PARSE_STATE_SIGNATURE_WAIT:
        // 把当前字节 c 存入 rxmsg->signature 中；
        rxmsg->signature[MAVLINK_SIGNATURE_BLOCK_LEN-status->signature_wait] = c;
        status->signature_wait--;
        // 进入签名校验；
        if (status->signature_wait == 0) {
#ifndef MAVLINK_NO_SIGNATURE_CHECK
            // 验证签名是否有效；
            //   - status->signing 是签名上下文（包含密钥等信息）；
            //   - status->signing_streams 是用于区分不同通道的签名信息；
            bool sig_ok = mavlink_signature_check(status->signing, status->signing_streams, rxmsg);
#else
            bool sig_ok = true;
#endif
                // 签名验证失败；
            if (!sig_ok &&
                // 设置了一个接受非签名消息的回调；
               (status->signing->accept_unsigned_callback &&
                status->signing->accept_unsigned_callback(status, rxmsg->msgid))) {
                // 网开一面接受这条消息；
                sig_ok = true;
            }
            // 根据签名验证结果，更新接收状态；
            if (sig_ok) {
                // 签名通过；
                status->msg_received = MAVLINK_FRAMING_OK;
            } else {
                // 签名失败，消息作废；
                status->msg_received = MAVLINK_FRAMING_BAD_SIGNATURE;
            }
            // 回到空闲状态，准备接收下一条消息；
            status->parse_state = MAVLINK_PARSE_STATE_IDLE;
            // 非空，外部提供了缓存空间；
            if (r_message !=NULL) {
                // 将接收到的完整消息复制出去供上层使用；
                memcpy(r_message, rxmsg, sizeof(mavlink_message_t));
            }
        }
        break;
    }

    // 当前接收的消息是完整、校验通过的（帧完整性 OK）；
    if (status->msg_received == MAVLINK_FRAMING_OK)
    {
        // 统计丢失了多少条消息（通过比对 current_seq 和 rxmsg->seq 的差距）；
        //while(status->current_seq != rxmsg->seq)
        //{
        //	status->packet_rx_drop_count++;
        //  status->current_seq++;
        //}
        // 更新消息序号和丢包统计；
        // 保存当前消息的序列号，seq 是 MAVLink 协议中用于检测丢包的字段，每条消息按序递增；
        status->current_rx_seq = rxmsg->seq;
        // 如果是第一次接收数据包，则将丢包计数器清零；
        if (status->packet_rx_success_count == 0) status->packet_rx_drop_count = 0;
        // 成功接收到的消息数量加一
        status->packet_rx_success_count++;
    }

    // 如果提供了消息缓冲区指针，就更新它；
    if (r_message != NULL) {
        // 给调用者返回当前消息的实际长度（注意：不是最大长度）；
        r_message->len = rxmsg->len; 
    }
    // 如果调用者提供了状态对象，就更新状态信息；
    if (r_mavlink_status != NULL) {	
        // 把当前解析器的内部状态 status 拷贝一份到 r_mavlink_status（供调用方使用）；
        r_mavlink_status->parse_state = status->parse_state;
        r_mavlink_status->packet_idx = status->packet_idx;
        r_mavlink_status->current_rx_seq = status->current_rx_seq+1;
        r_mavlink_status->packet_rx_success_count = status->packet_rx_success_count;
        r_mavlink_status->packet_rx_drop_count = status->parse_error;
        r_mavlink_status->flags = status->flags;
    }
    // 清空错误状态，准备解析下一条消息；
    status->parse_error = 0;

    // CRC 错误处理分支；
    if (status->msg_received == MAVLINK_FRAMING_BAD_CRC) {
        // 如果是 CRC 校验失败 的消息；
        if (r_message != NULL) {
            // 将收到的错误 CRC 值写入消息结构的 checksum 字段；
            r_message->checksum = rxmsg->ck[0] | (rxmsg->ck[1]<<8);
        }
    }

    return status->msg_received;
}

/**
 * This is a convenience function which handles the complete MAVLink parsing.
 * the function will parse one byte at a time and return the complete packet once
 * it could be successfully decoded. This function will return 0, 1 or
 * 2 (MAVLINK_FRAMING_INCOMPLETE, MAVLINK_FRAMING_OK or MAVLINK_FRAMING_BAD_CRC)
 *
 * Messages are parsed into an internal buffer (one for each channel). When a complete
 * message is received it is copies into *r_message and the channel's status is
 * copied into *r_mavlink_status.
 *
 * @param chan     ID of the channel to be parsed.
 *                 A channel is not a physical message channel like a serial port, but a logical partition of
 *                 the communication streams. COMM_NB is the limit for the number of channels
 *                 on MCU (e.g. ARM7), while COMM_NB_HIGH is the limit for the number of channels in Linux/Windows
 * @param c        The char to parse
 *
 * @param r_message NULL if no message could be decoded, otherwise the message data
 * @param r_mavlink_status if a message was decoded, this is filled with the channel's stats
 * @return 0 if no message could be decoded, 1 on good message and CRC, 2 on bad CRC
 *
 * A typical use scenario of this function call is:
 *
 * @code
 * #include <mavlink.h>
 *
 * mavlink_status_t status;
 * mavlink_message_t msg;
 * int chan = 0;
 *
 *
 * while(serial.bytesAvailable > 0)
 * {
 *   uint8_t byte = serial.getNextByte();
 *   if (mavlink_frame_char(chan, byte, &msg, &status) != MAVLINK_FRAMING_INCOMPLETE)
 *     {
 *     printf("Received message with ID %d, sequence: %d from component %d of system %d", msg.msgid, msg.seq, msg.compid, msg.sysid);
 *     }
 * }
 *
 *
 * @endcode
 */

// chan：第几号通道（比如无线电通道0、串口1之类的）；
// c：收到的一个字节；
// r_message：输出，存放解析成功的 MAVLink 消息；
// r_mavlink_status：输出，存放解析时的状态（比如解析到哪里了，出没出错）；
// 
// 处理这个字节，看看能不能拼出一条完整的 MAVLink 消息；
// 成功就把完整消息放到 r_message 里；
MAVLINK_HELPER uint8_t mavlink_frame_char(uint8_t chan, uint8_t c, mavlink_message_t* r_message, mavlink_status_t* r_mavlink_status)
{
    return mavlink_frame_char_buffer(mavlink_get_channel_buffer(chan),
                     mavlink_get_channel_status(chan),
                     c,
                     r_message,
                     r_mavlink_status);
}

/**
 * Set the protocol version
 */
MAVLINK_HELPER void mavlink_set_proto_version(uint8_t chan, unsigned int version)
{
    mavlink_status_t *status = mavlink_get_channel_status(chan);
    if (version > 1) {
        status->flags &= ~(MAVLINK_STATUS_FLAG_OUT_MAVLINK1);
    } else {
        status->flags |= MAVLINK_STATUS_FLAG_OUT_MAVLINK1;
    }
}

/**
 * Get the protocol version
 *
 * @return 1 for v1, 2 for v2
 */
MAVLINK_HELPER unsigned int mavlink_get_proto_version(uint8_t chan)
{
    mavlink_status_t *status = mavlink_get_channel_status(chan);
    if ((status->flags & MAVLINK_STATUS_FLAG_OUT_MAVLINK1) > 0) {
        return 1;
    } else {
        return 2;
    }
}

/**
 * This is a convenience function which handles the complete MAVLink parsing.
 * the function will parse one byte at a time and return the complete packet once
 * it could be successfully decoded. This function will return 0 or 1.
 *
 * Messages are parsed into an internal buffer (one for each channel). When a complete
 * message is received it is copies into *r_message and the channel's status is
 * copied into *r_mavlink_status.
 *
 * @param chan     ID of the channel to be parsed.
 *                 A channel is not a physical message channel like a serial port, but a logical partition of
 *                 the communication streams. COMM_NB is the limit for the number of channels
 *                 on MCU (e.g. ARM7), while COMM_NB_HIGH is the limit for the number of channels in Linux/Windows
 * @param c        The char to parse
 *
 * @param r_message NULL if no message could be decoded, otherwise the message data
 * @param r_mavlink_status if a message was decoded, this is filled with the channel's stats
 * @return 0 if no message could be decoded or bad CRC, 1 on good message and CRC
 *
 * A typical use scenario of this function call is:
 *
 * @code
 * #include <mavlink.h>
 *
 * mavlink_status_t status;
 * mavlink_message_t msg;
 * int chan = 0;
 *
 *
 * while(serial.bytesAvailable > 0)
 * {
 *   uint8_t byte = serial.getNextByte();
 *   if (mavlink_parse_char(chan, byte, &msg, &status))
 *     {
 *     printf("Received message with ID %d, sequence: %d from component %d of system %d", msg.msgid, msg.seq, msg.compid, msg.sysid);
 *     }
 * }
 *
 *
 * @endcode
 */

// - chan：哪个通道的（比如通道0、1、2）；
// - c：当前收到的1个字节；
// - r_message：成功时，把解析出来的消息结构体放这里；
// - r_mavlink_status：放解析时的状态，比如解析到哪里了，版本号啥的；
MAVLINK_HELPER uint8_t mavlink_parse_char(uint8_t chan, uint8_t c, mavlink_message_t* r_message, mavlink_status_t* r_mavlink_status)
{
    // 0：没有解析成功；
    // 1：成功解析出一条完整MAVLink包；
    // 其他值（比如 BAD_CRC、BAD_SIGNATURE）：错误了；
    // 
    // 处理当前这个字节 c，继续累积构建完整的 MAVLink 包；
    // msg_received 表示解析结果（成功/失败/出错）；
    uint8_t msg_received = mavlink_frame_char(chan, c, r_message, r_mavlink_status);
    // 校验和错了（BAD_CRC）；
    // 签名验证错了（BAD_SIGNATURE）那么要特别处理一下；
    if (msg_received == MAVLINK_FRAMING_BAD_CRC ||
        msg_received == MAVLINK_FRAMING_BAD_SIGNATURE) {
        // 当前通道上正在解析的那条消息缓存 rxmsg；
        mavlink_message_t* rxmsg = mavlink_get_channel_buffer(chan);
        // 当前通道的解析状态 status；
        mavlink_status_t* status = mavlink_get_channel_status(chan);
        _mav_parse_error(status);
        status->msg_received = MAVLINK_FRAMING_INCOMPLETE;
        status->parse_state = MAVLINK_PARSE_STATE_IDLE;
        if (c == MAVLINK_STX)
        {
            status->parse_state = MAVLINK_PARSE_STATE_GOT_STX;
            rxmsg->len = 0;
            mavlink_start_checksum(rxmsg);
        }
        return 0;
    }
    return msg_received;
}

/**
 * @brief Put a bitfield of length 1-32 bit into the buffer
 *
 * @param b the value to add, will be encoded in the bitfield
 * @param bits number of bits to use to encode b, e.g. 1 for boolean, 2, 3, etc.
 * @param packet_index the position in the packet (the index of the first byte to use)
 * @param bit_index the position in the byte (the index of the first bit to use)
 * @param buffer packet buffer to write into
 * @return new position of the last used byte in the buffer
 */
MAVLINK_HELPER uint8_t put_bitfield_n_by_index(int32_t b, uint8_t bits, uint8_t packet_index, uint8_t bit_index, uint8_t* r_bit_index, uint8_t* buffer)
{
    uint16_t bits_remain = bits;
    // Transform number into network order
    int32_t v;
    uint8_t i_bit_index, i_byte_index, curr_bits_n;
#if MAVLINK_NEED_BYTE_SWAP
    union {
        int32_t i;
        uint8_t b[4];
    } bin, bout;
    bin.i = b;
    bout.b[0] = bin.b[3];
    bout.b[1] = bin.b[2];
    bout.b[2] = bin.b[1];
    bout.b[3] = bin.b[0];
    v = bout.i;
#else
    v = b;
#endif

    // buffer in
    // 01100000 01000000 00000000 11110001
    // buffer out
    // 11110001 00000000 01000000 01100000

    // Existing partly filled byte (four free slots)
    // 0111xxxx

    // Mask n free bits
    // 00001111 = 2^0 + 2^1 + 2^2 + 2^3 = 2^n - 1
    // = ((uint32_t)(1 << n)) - 1; // = 2^n - 1

    // Shift n bits into the right position
    // out = in >> n;

    // Mask and shift bytes
    i_bit_index = bit_index;
    i_byte_index = packet_index;
    if (bit_index > 0)
    {
        // If bits were available at start, they were available
        // in the byte before the current index
        i_byte_index--;
    }

    // While bits have not been packed yet
    while (bits_remain > 0)
    {
        // Bits still have to be packed
        // there can be more than 8 bits, so
        // we might have to pack them into more than one byte

        // First pack everything we can into the current 'open' byte
        //curr_bits_n = bits_remain << 3; // Equals  bits_remain mod 8
        //FIXME
        if (bits_remain <= (uint8_t)(8 - i_bit_index))
        {
            // Enough space
            curr_bits_n = (uint8_t)bits_remain;
        }
        else
        {
            curr_bits_n = (8 - i_bit_index);
        }
        
        // Pack these n bits into the current byte
        // Mask out whatever was at that position with ones (xxx11111)
        buffer[i_byte_index] &= (0xFF >> (8 - curr_bits_n));
        // Put content to this position, by masking out the non-used part
        buffer[i_byte_index] |= ((0x00 << curr_bits_n) & v);
        
        // Increment the bit index
        i_bit_index += curr_bits_n;

        // Now proceed to the next byte, if necessary
        bits_remain -= curr_bits_n;
        if (bits_remain > 0)
        {
            // Offer another 8 bits / one byte
            i_byte_index++;
            i_bit_index = 0;
        }
    }
    
    *r_bit_index = i_bit_index;
    // If a partly filled byte is present, mark this as consumed
    if (i_bit_index != 7) i_byte_index++;
    return i_byte_index - packet_index;
}

#ifdef MAVLINK_USE_CONVENIENCE_FUNCTIONS

// To make MAVLink work on your MCU, define comm_send_ch() if you wish
// to send 1 byte at a time, or MAVLINK_SEND_UART_BYTES() to send a
// whole packet at a time

/*

#include "mavlink_types.h"

void comm_send_ch(mavlink_channel_t chan, uint8_t ch)
{
    if (chan == MAVLINK_COMM_0)
    {
        uart0_transmit(ch);
    }
    if (chan == MAVLINK_COMM_1)
    {
        uart1_transmit(ch);
    }
}
 */

MAVLINK_HELPER void _mavlink_send_uart(mavlink_channel_t chan, const char *buf, uint16_t len)
{
#ifdef MAVLINK_SEND_UART_BYTES
    /* this is the more efficient approach, if the platform
       defines it */
    MAVLINK_SEND_UART_BYTES(chan, (const uint8_t *)buf, len);
#else
    /* fallback to one byte at a time */
    uint16_t i;
    for (i = 0; i < len; i++) {
        comm_send_ch(chan, (uint8_t)buf[i]);
    }
#endif
}
#endif // MAVLINK_USE_CONVENIENCE_FUNCTIONS

#ifdef MAVLINK_USE_CXX_NAMESPACE
} // namespace mavlink
#endif
